package music

import java.io.File;

object Messages {
  
    /* Extendable Classes and their Unpackers (for case statements) */
  
  abstract class Msg extends Serializable{
    def myType:String = this.getClass().getCanonicalName().split('.').reverse(0);
  }
  
  trait Msg0 extends Msg { // Inheritors should be objects.
    override def toString:String = "Msg0";
  }
  class Msg1[T1](val _1:T1) extends Msg {
    override def toString:String = "Msg1(" + _1.toString + ")";

  }
  class Msg2[T1,T2](val _1:T1, val _2:T2) extends Msg {
   override def toString:String = "Msg2(" + _1.toString + "," + _2.toString + ")";
  } 
  
  class Msg3[T1,T2,T3](val _1:T1, val _2:T2, val _3:T3) extends Msg {
    override def toString:String = "Msg1(" + _1.toString + "," + _2.toString + "," + _3.toString + ")";
  }  
  
  class Msg4[T1,T2,T3,T4](val _1:T1, val _2:T2, val _3:T3, val _4:T4) extends Msg {
    override def toString:String = "Msg1(" + _1.toString + "," + _2.toString + "," + _3.toString + "," + _4.toString + ")";
  }
 
  class UnpackMsg1[T1 : Manifest, Companion <: Msg1[T1] : Manifest] {
    def apply(v:T1):Companion = {
      return manifest[Companion].erasure.getConstructor(
        manifest[T1].erasure
      ).newInstance(
        v.asInstanceOf[java.lang.Object]
      ).asInstanceOf[Companion];
    }
    def unapply(obj:Companion):Option[T1] = {
      if (obj eq null) {
        return None;
      }
      return Some(obj._1);
    }
  }
  class UnpackMsg2[
    T1 : Manifest,
    T2 : Manifest,
    Companion <: Msg2[T1,T2] : Manifest
  ] {
    def apply(v1:T1, v2:T2):Companion = {
      return manifest[Companion].erasure.getConstructor(
        manifest[T1].erasure,
        manifest[T2].erasure
      ).newInstance(
        v1.asInstanceOf[java.lang.Object],
        v2.asInstanceOf[java.lang.Object]
      ).asInstanceOf[Companion];
    }
    def unapply(obj:Companion):Option[Tuple2[T1,T2]] = {
      if (obj eq null) {
        return None;
      }
      return Some(obj._1, obj._2);
    }
  }

  class UnpackMsg3[
    T1 : Manifest,
    T2 : Manifest,
    T3 : Manifest,
    Companion <: Msg3[T1,T2,T3] : Manifest
  ] {
    def apply(v1:T1, v2:T2, v3:T3):Companion = {
      return manifest[Companion].erasure.getConstructor(
        manifest[T1].erasure,
        manifest[T2].erasure,
        manifest[T3].erasure
      ).newInstance(
        v1.asInstanceOf[java.lang.Object],
        v2.asInstanceOf[java.lang.Object],
        v3.asInstanceOf[java.lang.Object]
      ).asInstanceOf[Companion];
    }
    def unapply(obj:Companion):Option[Tuple3[T1,T2,T3]] = {
      if (obj eq null) {
        return None;
      }
      return Some(obj._1, obj._2, obj._3)
    }
  }
  

  class UnpackMsg4[
    T1 : Manifest,
    T2 : Manifest,
    T3 : Manifest,
    T4 : Manifest,
    Companion <: Msg4[T1,T2,T3,T4] : Manifest
  ] {
    def apply(v1:T1, v2:T2, v3:T3, v4:T4):Companion = {
      return manifest[Companion].erasure.getConstructor(
        manifest[T1].erasure,
        manifest[T2].erasure,
        manifest[T3].erasure,
        manifest[T4].erasure
      ).newInstance(
        v1.asInstanceOf[java.lang.Object],
        v2.asInstanceOf[java.lang.Object],
        v3.asInstanceOf[java.lang.Object],
        v4.asInstanceOf[java.lang.Object]
      ).asInstanceOf[Companion];
    }
    def unapply(obj:Companion):Option[Tuple4[T1,T2,T3,T4]] = {
      if (obj eq null) {
        return None;
      }
      return Some(obj._1, obj._2, obj._3, obj._4)
    }
  }
  
  /******** Actual Messages *********/
  
  class M_PLAY_SONG(val file:File) extends Msg1[File](file);
  object M_PLAY_SONG extends UnpackMsg1[File,M_PLAY_SONG];
  
  object M_STOP_SONG extends Msg0
  
  object M_PAUSE_SONG extends Msg0
  
  object M_RESUME_SONG extends Msg0
  
  object M_SONG_FINISHED extends Msg0
  
  class M_SEEK_SONG_S(val s:Double) extends Msg1[Double](s);
  object M_SEEK_SONG_S extends UnpackMsg1[Double,M_SEEK_SONG_S];
  
  class M_SEEK_SONG_PERCENT(val percent:Double) extends Msg1[Double](percent);
  object M_SEEK_SONG_PERCENT extends UnpackMsg1[Double, M_SEEK_SONG_PERCENT];
  
  class M_SET_VOLUME(val volume:Double) extends Msg1[Double](volume);
  object M_SET_VOLUME extends UnpackMsg1[Double, M_SET_VOLUME];
  
  
  
}